"""
Notes:
  Construct NetLogo string literals with *double* quotes only.
  The XML values must be quoted, so make a string value "&quot;like this&quot;"
  Put enumerated values in a *list* (even if there is only one).
  Use &gt; not >, etc, in metrics.
"""
import ConfigParser
from collections import OrderedDict

metrics = [
	'ticks',
	#count transmissions each tick by type
	'ntrans:road-flock',
	'ntrans:after-visit',
	'ntrans:visitor-flock',
	'ntrans:eggtrader-flock',
	'ntrans:livetrader-flock',
	'ntrans:arena-fc',
	'ntrans:fc-flock',
	#count contamination each tick by type
	'ncont:flock-livetrader',
	'ncont:flock-eggtrader',
	'ncont:flock-visitor',
	'ncont:flock-road',
	'ncont:flock-field',
	#categorize the birds by state
	's:bychickens', 's:fgducks', 's:byducks', 's:farms', 's:flocks', 's:poultry',
	'e:bychickens', 'e:byducks', 'e:fgducks', 'e:farms', 'e:flocks', 'e:poultry',
	'i:bychickens', 'i:byducks', 'i:fgducks', 'i:farms', 'i:flocks', 'i:poultry',
	'd:bychickens', 'r:fgducks', 'r:byducks', 'dr:farms', 'dr:flocks', 'dr:poultry',
    'count eggtraders',
    'count rice-fields with [ (field-state = "harvested") and (pcontam-until &gt; ticks)]',
	'count barns with [pcontam-until &gt; ticks]',
	'map [item 1 ?] table:to-list days-on-field',
    'table:to-list contaminations',
    ]


#the baseline enumerated value sets
#(note: some values reset by scenario choice!
#TODO: have them always set by scenario, never here)
defaultEnumeratedValueSets = OrderedDict([
    #default values of sliders and switches
    #(good practice is to explicitly set every slider etc)
	#set other globals in individual experiments, not here
    ('baseline', ["pert"]),
	('scenario', ["none"]),
	('infection-source', ['fgduck']),
	('shedding-period-chicken', [4]),
	('shedding-period-duck', [7]),
	('prop-fgd-barntofield', [0.6]),
	('n-cooperatives', [0]),
    ])




print """
Defaults
========
"""
for key, val in defaultEnumeratedValueSets.items():
	print("{:<30} : {}".format(key,val[0]))



class Experiment(object):
	def __init__(self,
		name,
		repetitions,
		runMetricsEveryStep,
		enumeratedValueSets,
		**sgf
		):
		self.name = name
		self.repetitions = repetitions
		self.runMetricsEveryStep = str(runMetricsEveryStep).lower()
		self.evs = enumeratedValueSets
		self.setup = sgf['setup']
		self.go = sgf['go']
		self.final = sgf['final']
	def header(self):
		fmt = '  <experiment name="{}" repetitions="{}" runMetricsEveryStep="true">'
		return fmt.format(self.name, self.repetitions, self.runMetricsEveryStep)
	def sgf(self):
		result = '    <setup>{}</setup>\n    <go>{}</go>'.format(self.setup, self.go)
		if self.final:
			result += '\n    <final>{}</final>'.format(self.final)
		return result
	def formatMetrics(self):
		return "\n".join(map(lambda x: "    <metric>{}</metric>".format(x), metrics))
	def enumeratedValueSets(self):
		kfmt = '    <enumeratedValueSet variable="{}">'
		vfmt = '      <value value="{}"/>'
		result = []
		for key,vals in self.evs.items():
			result.append( kfmt.format(key) )
			for val in vals:
				if isinstance(val,str):
					val = "&quot;{}&quot;".format(val)
				if isinstance(val,bool): #do not &quot;!
					val = str(val).lower()
				result.append(vfmt.format(val))
			result.append('    </enumeratedValueSet>')
		return '\n'.join(result)
	def toString(self):
		pieces = [
			self.header(),
			self.sgf(),
			self.formatMetrics(),
			self.enumeratedValueSets(),
            '  </experiment>'
		]
		return '\n'.join(pieces)




############  Get Experiments from experiments.ini  #######################
iniExperiments = list()
config = ConfigParser.ConfigParser()
config.optionxform = str  #retain capitalization
config.readfp( open('experiments.ini') )

for section in config.sections(): #each section is an experiment
	exp = dict()
	evs = defaultEnumeratedValueSets.copy()  #OrderedDict
	evs['experiment-name'] = [section]  #DO this; even tho info is already reported
	for option in config.options(section):
		option = option.strip()
		val = config.get(section, option).strip()
		if ',' in val:
			raise ValueError("no commas allowed in val: {}".format(val))
		if option == 'repetitions':
			exp[option] = int(val)
		elif option == 'runMetricsEveryStep':
			if val.lower() == 'true': val = True
			elif val.lower() == 'false': val = False
			else: raise ValueError('shd be bool')
			exp[option] = val
		elif option in ('setup', 'go', 'final'):
			exp[option] = val
		else:
			val = val.split()
			try:  #convert strings to numbers when appropriate
				fval = tuple( map(float, val) )
				ival = tuple( map(int, val) )
				if ival == fval: val = ival
				else: val = fval
			except ValueError: pass  #must be string vals
			evs[option] = val
	iniExperiments.append(
		Experiment(
			name = section,
			repetitions = exp['repetitions'],
			runMetricsEveryStep = exp['runMetricsEveryStep'],
			enumeratedValueSets = evs,
			setup = exp['setup'],
			go = exp['go'],
			final = exp['final']
		)
	)




sgf = dict(setup='setup',go='go', final='finalize')
###### pert baseline
baselineEVS = defaultEnumeratedValueSets.copy()

baselineXP = Experiment(
					name="BaselineModel",
					repetitions="100",
					runMetricsEveryStep=True,
					enumeratedValueSets=baselineEVS,
					**sgf)

###### roadInfectBYC
roadInfectBYC_EVS = defaultEnumeratedValueSets.copy()
roadInfectBYC_EVS.update({
    #chkchkchk
	})

roadInfectBYCXP = Experiment(
					name="roadInfectBYC",
					repetitions="100",
					runMetricsEveryStep=True,
					enumeratedValueSets=roadInfectBYC_EVS,
					**sgf)

###### roadPenProb
roadPenProb_EVS = defaultEnumeratedValueSets.copy()
roadPenProb_EVS.update({
    #chkchkchk
	})

roadPenProbXP = Experiment(
					name="roadPenProb",
					repetitions="100",
					runMetricsEveryStep=True,
					enumeratedValueSets=roadPenProb_EVS,
					**sgf)




def writeExperimentsToFile(experiments, fname):
	result = [   #xmlHeader 
		'<?xml version="1.0" encoding="us-ascii"?>',
		'<!DOCTYPE experiments SYSTEM "behaviorspace.dtd">',
		'<experiments>',
	]
	result.extend(map(lambda x: x.toString(), experiments))
	result.append('</experiments>')
	with open(fname,'w') as fout:	
		fout.write('\n'.join(result))

writeExperimentsToFile(iniExperiments, "birdflu-experiments.xml")

print """
Deviations from Defaults
========================
"""
for exp in iniExperiments:
	print
	print exp.name
	evs = exp.evs
	for key,val in evs.items():
		vfmt = ", ".join(["{}"] * len(val))
		fmt = "  {:<30} : " + vfmt
		if key in baselineXP.evs:
			if baselineXP.evs[key] != evs[key]:
				print( fmt.format(key,*val) )
		elif key != "experiment-name":
			print( fmt.format(key,*val) )




